using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Lily.Example.Forms.TestHelper.UI;

namespace Lily.TestHelper.UI
{
	public class InputForm<TFormType>
	{
		private readonly IWindowDriver _windowDriver;
		protected readonly LinkedList<IInputTester> _inputTesters = new LinkedList<IInputTester>();
		private const string DefaultSubmitName = "Save";

		public InputForm(IWindowDriver windowDriver)
		{
			_windowDriver = windowDriver;
		}

		private LinkedList<IInputTester> InputTesters
		{
			get { return _inputTesters; }
		}

		public InputForm<TFormType> Input(Expression<Func<TFormType, object>> expression, string text)
		{
			PropertyInfo propertyInfo = ReflectionHelper.FindPropertyFromExpression(expression);

			IInputTesterFactory factory = GetInputForProperty(propertyInfo);

			return Input(factory.Create(expression, text));
		}

		public InputForm<TFormType> Input(Expression<Func<TFormType, object>> expression, bool choice)
		{
			PropertyInfo propertyInfo = ReflectionHelper.FindPropertyFromExpression(expression);

			IInputTesterFactory factory = GetInputForProperty(propertyInfo);

			return Input(factory.Create(expression, choice.ToString()));
		}

		public InputForm<TFormType> Input(Expression<Func<TFormType, object>> expression, params string[] text)
		{
			PropertyInfo propertyInfo = ReflectionHelper.FindPropertyFromExpression(expression);

			IMultipleInputTesterFactory factory = GetInputForMultipleProperty(propertyInfo);

			return Input(factory.Create(expression, text));
		}

		private IInputTesterFactory GetInputForProperty(PropertyInfo propertyInfo)
		{
			return InputTesterFactory.Default().Where(factory => factory.CanHandle(propertyInfo)).First();
		}

		private IMultipleInputTesterFactory GetInputForMultipleProperty(PropertyInfo propertyInfo)
		{
			return MultipleInputTesterFactory.Default().Where(factory => factory.CanHandle(propertyInfo)).First();
		}

		public InputForm<TFormType> Input(IInputTester tester)
		{
			_inputTesters.AddLast(tester);
			return this;
		}

		public IWindowDriver Submit(string buttonName)
		{
			SetInputs();

			_windowDriver.ClickButton(buttonName);
			_inputTesters.Clear();
			return _windowDriver;
		}


		public IWindowDriver Save()
		{
			return Submit(DefaultSubmitName);
		}

		private void SetInputs()
		{
			_inputTesters.ForEach(x => x.SetInput(_windowDriver));
		}

		public IWindowDriver WindowDriver
		{
			get
			{
				return _windowDriver;
			}
		}

		public IWindowDriver Assert()
		{
			AssertInputs();
			return _windowDriver;
		}

		private void AssertInputs()
		{
			_inputTesters.ForEach(x => x.AssertInputValueMatches(_windowDriver));
		}

	}
}